#include "stdafx.h"
#include "ApiReader.h"
#include "Architecture.h"
#include "SystemInformation.h"
#include "StringConversion.h"
#include "PeParser.h"

stdext::hash_multimap<DWORD_PTR, ApiInfo *> ApiReader::apiList; //api look up table
std::map<DWORD_PTR, ImportModuleThunk> *  ApiReader::moduleThunkList; //store found apis

DWORD_PTR ApiReader::minApiAddress = 0xFFFFFFFF;
DWORD_PTR ApiReader::maxApiAddress = 0;

//#define DEBUG_COMMENTS

void ApiReader::readApisFromModuleList()
{

    readExportTableAlwaysFromDisk = true;

    for (unsigned int i = 0; i < moduleList.size(); i++)
    {
        setModulePriority(&moduleList[i]);

        if (moduleList[i].modBaseAddr + moduleList[i].modBaseSize > maxValidAddress)
        {
            maxValidAddress = moduleList[i].modBaseAddr + moduleList[i].modBaseSize;
        }

//		Scylla::windowLog.log(L"Module parsing: %s",moduleList[i].fullPath);

        if (!moduleList[i].isAlreadyParsed)
        {
            parseModule(&moduleList[i]);
        }
    }

#ifdef DEBUG_COMMENTS
    Scylla::debugLog.log(L"Address Min " PRINTF_DWORD_PTR_FULL L" Max " PRINTF_DWORD_PTR_FULL L"\nimagebase " PRINTF_DWORD_PTR_FULL L" maxValidAddress " PRINTF_DWORD_PTR_FULL, minApiAddress, maxApiAddress, targetImageBase ,maxValidAddress);
#endif
}

void ApiReader::parseModule(ModuleInfo *module)
{
    module->parsing = true;

    if (isWinSxSModule(module))
    {
        parseModuleWithMapping(module);
    }
    else if (isModuleLoadedInOwnProcess(module))
    {
        parseModuleWithOwnProcess(module);
    }
    else
    {
        if (readExportTableAlwaysFromDisk)
        {
            parseModuleWithMapping(module);
        }
        else
        {
            parseModuleWithProcess(module);
        }
    }

    module->isAlreadyParsed = true;
}

void ApiReader::parseModuleWithMapping(ModuleInfo *moduleInfo)
{
    LPVOID fileMapping = 0;
    PIMAGE_NT_HEADERS pNtHeader = 0;
    PIMAGE_DOS_HEADER pDosHeader = 0;

    fileMapping = createFileMappingViewRead(moduleInfo->fullPath);

    if (fileMapping == 0)
        return;

    pDosHeader = (PIMAGE_DOS_HEADER)fileMapping;
    pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileMapping + (DWORD_PTR)(pDosHeader->e_lfanew));

    if (isPeAndExportTableValid(pNtHeader))
    {
        parseExportTable(moduleInfo, pNtHeader, (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)fileMapping + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress), (DWORD_PTR)fileMapping);
    }


    UnmapViewOfFile(fileMapping);

}

inline bool ApiReader::isApiForwarded(DWORD_PTR rva, PIMAGE_NT_HEADERS pNtHeader)
{
    if ((rva > pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) && (rva < (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)))
    {
        return true;
    }
    else
    {
        return false;
    }
}

void ApiReader::handleForwardedApi(DWORD_PTR vaStringPointer,char * functionNameParent, DWORD_PTR rvaParent, WORD ordinalParent, ModuleInfo *moduleParent)
{
    size_t dllNameLength = 0;
    WORD ordinal = 0;
    ModuleInfo *module = 0;
    DWORD_PTR vaApi = 0;
    DWORD_PTR rvaApi = 0;
    char dllName[100] = {0};
    WCHAR dllNameW[100] = {0};
    char *fordwardedString = (char *)vaStringPointer;
    char *searchFunctionName = strchr(fordwardedString, '.');


    if (!searchFunctionName)
        return;

    dllNameLength = searchFunctionName - fordwardedString;

    if (dllNameLength >= 99)
    {
        return;
    }
    else
    {
        strncpy_s(dllName, fordwardedString, dllNameLength);
    }

    searchFunctionName++;

    //Since Windows 7
    if (!_strnicmp(dllName, "API-", 4) || !_strnicmp(dllName, "EXT-", 4)) //API_SET_PREFIX_NAME, API_SET_EXTENSION
    {
        /*
            Info: http://www.nirsoft.net/articles/windows_7_kernel_architecture_changes.html
        */

        FARPROC addy = GetProcAddress(GetModuleHandleA(dllName), searchFunctionName);

        if (addy != 0)
        {
            addApi(functionNameParent,0, ordinalParent, (DWORD_PTR)addy, (DWORD_PTR)addy - (DWORD_PTR)GetModuleHandleA(dllName), true, moduleParent);
        }

        return;
    }

    strcat_s(dllName, ".dll");

    StringConversion::ToUTF16(dllName, dllNameW, _countof(dllNameW));

    if (!_wcsicmp(dllNameW, moduleParent->getFilename()))
    {
        module = moduleParent;
    }
    else
    {
        module = findModuleByName(dllNameW);
    }

    if (module != 0) // module == 0 -> can be ignored
    {
        /*if ((module->isAlreadyParsed == false) && (module != moduleParent))
        {
        	//do API extract

        	if (module->parsing == true)
        	{
        		//some stupid circle dependency
        		printf("stupid circle dependency %s\n",module->getFilename());
        	}
        	else
        	{
        		parseModule(module);
        	}
        }*/

        if (strchr(searchFunctionName,'#'))
        {
            //forwarding by ordinal
            searchFunctionName++;
            ordinal = (WORD)atoi(searchFunctionName);
            findApiByModuleAndOrdinal(module, ordinal, &vaApi, &rvaApi);
        }
        else
        {
            findApiByModuleAndName(module, searchFunctionName, &vaApi, &rvaApi);
        }

        if (rvaApi == 0)
        {
#ifdef DEBUG_COMMENTS
            Scylla::debugLog.log(L"handleForwardedApi :: Api not found, this is really BAD! %S",fordwardedString);
#endif
        }
        else
        {
            addApi(functionNameParent,0, ordinalParent, vaApi, rvaApi, true, moduleParent);
        }
    }

}

ModuleInfo * ApiReader::findModuleByName(WCHAR *name)
{
    for (unsigned int i = 0; i < moduleList.size(); i++) {
        if (!_wcsicmp(moduleList[i].getFilename(), name))
        {
            return &moduleList[i];
        }
    }

    return 0;
}

void ApiReader::addApiWithoutName(WORD ordinal, DWORD_PTR va, DWORD_PTR rva,bool isForwarded, ModuleInfo *moduleInfo)
{
    addApi(0, 0, ordinal, va, rva, isForwarded, moduleInfo);
}

void ApiReader::addApi(char *functionName, WORD hint, WORD ordinal, DWORD_PTR va, DWORD_PTR rva, bool isForwarded, ModuleInfo *moduleInfo)
{
    ApiInfo *apiInfo = new ApiInfo();

    if ((functionName != 0) && (strlen(functionName) < _countof(apiInfo->name)))
    {
        strcpy_s(apiInfo->name, functionName);
    }
    else
    {
        apiInfo->name[0] = 0x00;
    }

    apiInfo->ordinal = ordinal;
    apiInfo->isForwarded = isForwarded;
    apiInfo->module = moduleInfo;
    apiInfo->rva = rva;
    apiInfo->va = va;
    apiInfo->hint = hint;

    setMinMaxApiAddress(va);

    moduleInfo->apiList.push_back(apiInfo);

    apiList.insert(API_Pair(va, apiInfo));
}

BYTE * ApiReader::getHeaderFromProcess(ModuleInfo * module)
{
    BYTE *bufferHeader = 0;
    DWORD readSize = 0;

    if (module->modBaseSize < PE_HEADER_BYTES_COUNT)
    {
        readSize = module->modBaseSize;
    }
    else
    {
        readSize = PE_HEADER_BYTES_COUNT;
    }

    bufferHeader = new BYTE[readSize];

    if(!readMemoryFromProcess(module->modBaseAddr, readSize, bufferHeader))
    {
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"getHeaderFromProcess :: Error reading header");
#endif
        delete[] bufferHeader;
        return 0;
    }
    else
    {
        return bufferHeader;
    }
}

BYTE * ApiReader::getExportTableFromProcess(ModuleInfo * module, PIMAGE_NT_HEADERS pNtHeader)
{
    DWORD readSize = 0;
    BYTE *bufferExportTable = 0;

    readSize = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;

    if (readSize < (sizeof(IMAGE_EXPORT_DIRECTORY) + 8))
    {
        //Something is wrong with the PE Header
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"Something is wrong with the PE Header here Export table size %d", readSize);
#endif
        readSize = sizeof(IMAGE_EXPORT_DIRECTORY) + 100;
    }

    if (readSize)
    {
        bufferExportTable = new BYTE[readSize];

        if (!bufferExportTable)
        {
#ifdef DEBUG_COMMENTS
            Scylla::debugLog.log(L"Something is wrong with the PE Header here Export table size %d", readSize);
#endif
            return 0;
        }

        if(!readMemoryFromProcess(module->modBaseAddr + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, readSize, bufferExportTable))
        {
#ifdef DEBUG_COMMENTS
            Scylla::debugLog.log(L"getExportTableFromProcess :: Error reading export table from process");
#endif
            delete[] bufferExportTable;
            return 0;
        }
        else
        {
            return bufferExportTable;
        }
    }
    else
    {
        return 0;
    }
}

void ApiReader::parseModuleWithProcess(ModuleInfo * module)
{
    PIMAGE_NT_HEADERS pNtHeader = 0;
    PIMAGE_DOS_HEADER pDosHeader = 0;
    BYTE *bufferHeader = 0;
    BYTE *bufferExportTable = 0;
    PeParser peParser(module->modBaseAddr, false);

    if (!peParser.isValidPeFile())
        return;

    pNtHeader = peParser.getCurrentNtHeader();

    if (peParser.hasExportDirectory())
    {
        bufferExportTable = getExportTableFromProcess(module, pNtHeader);

        if(bufferExportTable)
        {
            parseExportTable(module,pNtHeader,(PIMAGE_EXPORT_DIRECTORY)bufferExportTable, (DWORD_PTR)bufferExportTable - pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
            delete[] bufferExportTable;
        }
    }
}

void ApiReader::parseExportTable(ModuleInfo *module, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress)
{
    DWORD *addressOfFunctionsArray = 0,*addressOfNamesArray = 0;
    WORD *addressOfNameOrdinalsArray = 0;
    char *functionName = 0;
    DWORD_PTR RVA = 0, VA = 0;
    WORD ordinal = 0;
    WORD i = 0, j = 0;
    bool withoutName;


    addressOfFunctionsArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfFunctions + deltaAddress);
    addressOfNamesArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfNames + deltaAddress);
    addressOfNameOrdinalsArray = (WORD *)((DWORD_PTR)pExportDir->AddressOfNameOrdinals + deltaAddress);

#ifdef DEBUG_COMMENTS
    Scylla::debugLog.log(L"parseExportTable :: module %s NumberOfNames %X", module->fullPath, pExportDir->NumberOfNames);
#endif

    for (i = 0; i < pExportDir->NumberOfNames; i++)
    {
        functionName = (char*)(addressOfNamesArray[i] + deltaAddress);
        ordinal = (WORD)(addressOfNameOrdinalsArray[i] + pExportDir->Base);
        RVA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]];
        VA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]] + module->modBaseAddr;

#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"parseExportTable :: api %S ordinal %d imagebase " PRINTF_DWORD_PTR_FULL L" RVA " PRINTF_DWORD_PTR_FULL L" VA " PRINTF_DWORD_PTR_FULL, functionName, ordinal, module->modBaseAddr, RVA, VA);
#endif
        if (!isApiBlacklisted(functionName))
        {
            if (!isApiForwarded(RVA,pNtHeader))
            {
                addApi(functionName, i, ordinal,VA,RVA,false,module);
            }
            else
            {
                //printf("Forwarded: %s\n",functionName);
                handleForwardedApi(RVA + deltaAddress,functionName,RVA,ordinal,module);
            }
        }

    }

    /*Exports without name*/
    if (pExportDir->NumberOfNames != pExportDir->NumberOfFunctions)
    {
        for (i = 0; i < pExportDir->NumberOfFunctions; i++)
        {
            withoutName = true;
            for (j = 0; j < pExportDir->NumberOfNames; j++)
            {
                if(addressOfNameOrdinalsArray[j] == i)
                {
                    withoutName = false;
                    break;
                }
            }
            if (withoutName && addressOfFunctionsArray[i] != 0)
            {
                ordinal = (WORD)(i+pExportDir->Base);
                RVA = addressOfFunctionsArray[i];
                VA = (addressOfFunctionsArray[i] + module->modBaseAddr);


                if (!isApiForwarded(RVA,pNtHeader))
                {
                    addApiWithoutName(ordinal,VA,RVA,false,module);
                }
                else
                {
                    handleForwardedApi(RVA + deltaAddress,0,RVA,ordinal,module);
                }

            }
        }
    }
}

void ApiReader::findApiByModuleAndOrdinal(ModuleInfo * module, WORD ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi)
{
    findApiByModule(module,0,ordinal,vaApi,rvaApi);
}

void ApiReader::findApiByModuleAndName(ModuleInfo * module, char * searchFunctionName, DWORD_PTR * vaApi, DWORD_PTR * rvaApi)
{
    findApiByModule(module,searchFunctionName,0,vaApi,rvaApi);
}

void ApiReader::findApiByModule(ModuleInfo * module, char * searchFunctionName, WORD ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi)
{
    if (isModuleLoadedInOwnProcess(module))
    {
        HMODULE hModule = GetModuleHandle(module->getFilename());

        if (hModule)
        {
            if (vaApi)
            {
                if (ordinal)
                {
                    *vaApi = (DWORD_PTR)GetProcAddress(hModule, (LPCSTR)ordinal);
                }
                else
                {
                    *vaApi = (DWORD_PTR)GetProcAddress(hModule, searchFunctionName);
                }

                *rvaApi = (*vaApi) - (DWORD_PTR)hModule;
                *vaApi = (*rvaApi) + module->modBaseAddr;
            }
            else
            {
#ifdef DEBUG_COMMENTS
                Scylla::debugLog.log(L"findApiByModule :: vaApi == NULL, should never happen %S", searchFunctionName);
#endif
            }
        }
        else
        {
#ifdef DEBUG_COMMENTS
            Scylla::debugLog.log(L"findApiByModule :: hModule == NULL, should never happen %s", module->getFilename());
#endif
        }
    }
    else
    {
        //search api in extern process
        findApiInProcess(module,searchFunctionName,ordinal,vaApi,rvaApi);
    }
}

bool ApiReader::isModuleLoadedInOwnProcess(ModuleInfo * module)
{
    for (unsigned int i = 0; i < ownModuleList.size(); i++)
    {
        if (!_wcsicmp(module->fullPath, ownModuleList[i].fullPath))
        {
            //printf("isModuleLoadedInOwnProcess :: %s %s\n",module->fullPath,ownModuleList[i].fullPath);
            return true;
        }
    }
    return false;
}

void ApiReader::parseModuleWithOwnProcess( ModuleInfo * module )
{
    PIMAGE_NT_HEADERS pNtHeader = 0;
    PIMAGE_DOS_HEADER pDosHeader = 0;
    HMODULE hModule = GetModuleHandle(module->getFilename());

    if (hModule)
    {
        pDosHeader = (PIMAGE_DOS_HEADER)hModule;
        pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)hModule + (DWORD_PTR)(pDosHeader->e_lfanew));

        if (isPeAndExportTableValid(pNtHeader))
        {
            parseExportTable(module, pNtHeader, (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)hModule + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress), (DWORD_PTR)hModule);
        }
    }
    else
    {
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"parseModuleWithOwnProcess :: hModule is NULL");
#endif
    }
}

bool ApiReader::isPeAndExportTableValid(PIMAGE_NT_HEADERS pNtHeader)
{
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
//		Scylla::windowLog.log(L"-> IMAGE_NT_SIGNATURE doesn't match.");
        return false;
    }
    else if ((pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) || (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0))
    {
//		Scylla::windowLog.log(L"-> No export table.");
        return false;
    }
    else
    {
        return true;
    }
}

void ApiReader::findApiInProcess(ModuleInfo * module, char * searchFunctionName, WORD ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi)
{
    PIMAGE_NT_HEADERS pNtHeader = 0;
    PIMAGE_DOS_HEADER pDosHeader = 0;
    BYTE *bufferHeader = 0;
    BYTE *bufferExportTable = 0;


    bufferHeader = getHeaderFromProcess(module);

    if (bufferHeader == 0)
        return;

    pDosHeader = (PIMAGE_DOS_HEADER)bufferHeader;
    pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)bufferHeader + (DWORD_PTR)(pDosHeader->e_lfanew));

    if (isPeAndExportTableValid(pNtHeader))
    {
        bufferExportTable = getExportTableFromProcess(module, pNtHeader);

        if(bufferExportTable)
        {
            findApiInExportTable(module,(PIMAGE_EXPORT_DIRECTORY)bufferExportTable, (DWORD_PTR)bufferExportTable - pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress,searchFunctionName,ordinal,vaApi,rvaApi);
            delete[] bufferExportTable;
        }
    }

    delete[] bufferHeader;
}

bool ApiReader::findApiInExportTable(ModuleInfo *module, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress, char * searchFunctionName, WORD ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi)
{
    DWORD *addressOfFunctionsArray = 0,*addressOfNamesArray = 0;
    WORD *addressOfNameOrdinalsArray = 0;
    char *functionName = 0;
    DWORD i = 0, j = 0;

    addressOfFunctionsArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfFunctions + deltaAddress);
    addressOfNamesArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfNames + deltaAddress);
    addressOfNameOrdinalsArray = (WORD *)((DWORD_PTR)pExportDir->AddressOfNameOrdinals + deltaAddress);

    if (searchFunctionName)
    {
        for (i = 0; i < pExportDir->NumberOfNames; i++)
        {
            functionName = (char*)(addressOfNamesArray[i] + deltaAddress);

            if (!strcmp(functionName,searchFunctionName))
            {
                *rvaApi = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]];
                *vaApi = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]] + module->modBaseAddr;
                return true;
            }
        }
    }
    else
    {
        for (i = 0; i < pExportDir->NumberOfFunctions; i++)
        {
            if (ordinal == (i+pExportDir->Base))
            {
                *rvaApi = addressOfFunctionsArray[i];
                *vaApi = (addressOfFunctionsArray[i] + module->modBaseAddr);
                return true;
            }
        }
    }

    return false;
}


void ApiReader::setModulePriority(ModuleInfo * module)
{
    const WCHAR *moduleFileName = module->getFilename();

    //imports by kernelbase don't exist
    if (!_wcsicmp(moduleFileName, L"kernelbase.dll"))
    {
        module->priority = -1;
    }
    else if (!_wcsicmp(moduleFileName, L"ntdll.dll"))
    {
        module->priority = 0;
    }
    else if (!_wcsicmp(moduleFileName, L"shlwapi.dll"))
    {
        module->priority = 0;
    }
    else if (!_wcsicmp(moduleFileName, L"ShimEng.dll"))
    {
        module->priority = 0;
    }
    else if (!_wcsicmp(moduleFileName, L"kernel32.dll"))
    {
        module->priority = 2;
    }
    else if (!_wcsnicmp(moduleFileName, L"API-", 4) || !_wcsnicmp(moduleFileName, L"EXT-", 4)) //API_SET_PREFIX_NAME, API_SET_EXTENSION
    {
        module->priority = 0;
    }
    else
    {
        module->priority = 1;
    }
}

bool ApiReader::isApiAddressValid(DWORD_PTR virtualAddress)
{
    return apiList.count(virtualAddress) > 0;
}

ApiInfo * ApiReader::getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect)
{
    stdext::hash_multimap<DWORD_PTR, ApiInfo *>::iterator it1, it2;
    size_t c = 0;
    size_t countDuplicates = apiList.count(virtualAddress);
    int countHighPriority = 0;
    ApiInfo *apiFound = 0;


    if (countDuplicates == 0)
    {
        return 0;
    }
    else if (countDuplicates == 1)
    {
        //API is 100% correct
        *isSuspect = false;
        it1 = apiList.find(virtualAddress); // Find first match.
        return (ApiInfo *)((*it1).second);
    }
    else
    {
        it1 = apiList.find(virtualAddress); // Find first match.

        //any high priority with a name
        apiFound = getScoredApi(it1,countDuplicates,true,false,false,true,false,false,false,false);

        if (apiFound)
            return apiFound;

        *isSuspect = true;

        //high priority with a name and ansi/unicode name
        apiFound = getScoredApi(it1,countDuplicates,true,true,false,true,false,false,false,false);

        if (apiFound)
            return apiFound;

        //priority 2 with no underline in name
        apiFound = getScoredApi(it1,countDuplicates,true,false,true,false,false,false,true,false);

        if (apiFound)
            return apiFound;

        //priority 1 with a name
        apiFound = getScoredApi(it1,countDuplicates,true,false,false,false,false,true,false,false);

        if (apiFound)
            return apiFound;

        //With a name
        apiFound = getScoredApi(it1,countDuplicates,true,false,false,false,false,false,false,false);

        if (apiFound)
            return apiFound;

        //any with priority, name, ansi/unicode
        apiFound = getScoredApi(it1,countDuplicates,true,true,false,true,false,false,false,true);

        if (apiFound)
            return apiFound;

        //any with priority
        apiFound = getScoredApi(it1,countDuplicates,false,false,false,true,false,false,false,true);

        if (apiFound)
            return apiFound;

        //has prio 0 and name
        apiFound = getScoredApi(it1,countDuplicates,false,false,false,false,true,false,false,true);

        if (apiFound)
            return apiFound;
    }

    //is never reached
//	Scylla::windowLog.log(L"getApiByVirtualAddress :: There is a api resolving bug, VA: " PRINTF_DWORD_PTR_FULL, virtualAddress);
    for (size_t c = 0; c < countDuplicates; c++, it1++)
    {
        apiFound = (ApiInfo *)((*it1).second);
//		Scylla::windowLog.log(L"-> Possible API: %S ord: %d ", apiFound->name, apiFound->ordinal);
    }
    return (ApiInfo *) 1;
}

ApiInfo * ApiReader::getScoredApi(stdext::hash_multimap<DWORD_PTR, ApiInfo *>::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin )
{
    ApiInfo * foundApi = 0;
    ApiInfo * foundMatchingApi = 0;
    int countFoundApis = 0;
    int scoreNeeded = 0;
    int scoreValue = 0;
    size_t apiNameLength = 0;

    if (hasUnicodeAnsiName || hasNoUnderlineInName)
    {
        hasName = true;
    }

    if (hasName)
        scoreNeeded++;

    if (hasUnicodeAnsiName)
        scoreNeeded++;

    if (hasNoUnderlineInName)
        scoreNeeded++;

    if (hasPrioDll)
        scoreNeeded++;

    if (hasPrio0Dll)
        scoreNeeded++;

    if (hasPrio1Dll)
        scoreNeeded++;

    if (hasPrio2Dll)
        scoreNeeded++;

    for (size_t c = 0; c < countDuplicates; c++, it1++)
    {
        foundApi = (ApiInfo *)((*it1).second);
        scoreValue = 0;

        if (hasName)
        {
            if (foundApi->name[0] != 0x00)
            {
                scoreValue++;

                if (hasUnicodeAnsiName)
                {
                    apiNameLength = strlen(foundApi->name);

                    if ((foundApi->name[apiNameLength - 1] == 'W') || (foundApi->name[apiNameLength - 1] == 'A'))
                    {
                        scoreValue++;
                    }
                }

                if (hasNoUnderlineInName)
                {
                    if (!strrchr(foundApi->name, '_'))
                    {
                        scoreValue++;
                    }
                }
            }
        }

        if (hasPrioDll)
        {
            if (foundApi->module->priority >= 1)
            {
                scoreValue++;
            }
        }

        if (hasPrio0Dll)
        {
            if (foundApi->module->priority == 0)
            {
                scoreValue++;
            }
        }

        if (hasPrio1Dll)
        {
            if (foundApi->module->priority == 1)
            {
                scoreValue++;
            }
        }

        if (hasPrio2Dll)
        {
            if (foundApi->module->priority == 2)
            {
                scoreValue++;
            }
        }


        if (scoreValue == scoreNeeded)
        {
            foundMatchingApi = foundApi;
            countFoundApis++;

            if (firstWin)
            {
                return foundMatchingApi;
            }
        }
    }

    if (countFoundApis == 1)
    {
        return foundMatchingApi;
    }
    else
    {
        return (ApiInfo *)0;
    }

}

void ApiReader::setMinMaxApiAddress(DWORD_PTR virtualAddress)
{
    if (virtualAddress < minApiAddress)
    {
        minApiAddress = virtualAddress - 1;
    }
    if (virtualAddress > maxApiAddress)
    {
        maxApiAddress = virtualAddress + 1;
    }
}

void  ApiReader::readAndParseIAT(DWORD_PTR addressIAT, DWORD sizeIAT, std::map<DWORD_PTR, ImportModuleThunk> &moduleListNew)
{
    moduleThunkList = &moduleListNew;
    BYTE *dataIat = new BYTE[sizeIAT];
    if (readMemoryFromProcess(addressIAT,sizeIAT,dataIat))
    {
        parseIAT(addressIAT,dataIat,sizeIAT);
    }
    else
    {
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"ApiReader::readAndParseIAT :: error reading iat " PRINTF_DWORD_PTR_FULL, addressIAT);
#endif
    }

    delete[] dataIat;
}

void ApiReader::parseIAT(DWORD_PTR addressIAT, BYTE * iatBuffer, SIZE_T size)
{
    ApiInfo *apiFound = 0;
    ModuleInfo *module = 0;
    bool isSuspect = false;
    int countApiFound = 0, countApiNotFound = 0;
    DWORD_PTR * pIATAddress = (DWORD_PTR *)iatBuffer;
    SIZE_T sizeIAT = size / sizeof(DWORD_PTR);

    for (SIZE_T i = 0; i < sizeIAT; i++)
    {
        //Scylla::windowLog.log(L"%08X %08X %d von %d", addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, pIATAddress[i],i,sizeIAT);

        if (!isInvalidMemoryForIat(pIATAddress[i]))
        {
            if ( (pIATAddress[i] > minApiAddress) && (pIATAddress[i] < maxApiAddress) )
            {

                apiFound = getApiByVirtualAddress(pIATAddress[i], &isSuspect);
                if (apiFound == 0)
                {
                    //Scylla::windowLog.log(L"getApiByVirtualAddress :: No Api found " PRINTF_DWORD_PTR_FULL, pIATAddress[i]);
                }
                if (apiFound == (ApiInfo *)1)
                {
#ifdef DEBUG_COMMENTS
                    Scylla::debugLog.log(L"apiFound == (ApiInfo *)1 -> " PRINTF_DWORD_PTR_FULL, pIATAddress[i]);
#endif
                }
                else if (apiFound)
                {
                    countApiFound++;
#ifdef DEBUG_COMMENTS
                    Scylla::debugLog.log(PRINTF_DWORD_PTR_FULL L" %s %d %s", apiFound->va, apiFound->module->getFilename(), apiFound->ordinal, apiFound->name);
#endif
                    if (module != apiFound->module)
                    {
                        module = apiFound->module;
                        addFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, apiFound, true, isSuspect);
                    }
                    else
                    {
                        addFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, apiFound, false, isSuspect);
                    }

                }
                else
                {
                    countApiNotFound++;
                    addNotFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, pIATAddress[i]);
                    //printf("parseIAT :: API not found %08X\n", pIATAddress[i]);
                }
            }
            else
            {
                //printf("parseIAT :: API not found %08X\n", pIATAddress[i]);
                countApiNotFound++;
                addNotFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, pIATAddress[i]);
            }
        }

    }

    //Scylla::windowLog.log(L"IAT parsing finished, found %d valid APIs, missed %d APIs", countApiFound, countApiNotFound);
}

void ApiReader::addFoundApiToModuleList(DWORD_PTR iatAddressVA, ApiInfo * apiFound, bool isNewModule, bool isSuspect)
{
    if (isNewModule)
    {
        addModuleToModuleList(apiFound->module->getFilename(), iatAddressVA - targetImageBase);
    }
    addFunctionToModuleList(apiFound, iatAddressVA, iatAddressVA - targetImageBase, apiFound->ordinal, true, isSuspect);
}

bool ApiReader::addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk)
{
    ImportModuleThunk module;

    module.firstThunk = firstThunk;
    wcscpy_s(module.moduleName, moduleName);

    (*moduleThunkList).insert(std::pair<DWORD_PTR,ImportModuleThunk>(firstThunk,module));

    return true;
}

void ApiReader::addUnknownModuleToModuleList(DWORD_PTR firstThunk)
{
    ImportModuleThunk module;

    module.firstThunk = firstThunk;
    wcscpy_s(module.moduleName, L"?");

    (*moduleThunkList).insert(std::pair<DWORD_PTR,ImportModuleThunk>(firstThunk,module));
}

bool ApiReader::addFunctionToModuleList(ApiInfo * apiFound, DWORD_PTR va, DWORD_PTR rva, WORD ordinal, bool valid, bool suspect)
{
    ImportThunk import;
    ImportModuleThunk  * module = 0;
    std::map<DWORD_PTR, ImportModuleThunk>::iterator iterator1;

    if ((*moduleThunkList).size() > 1)
    {
        iterator1 = (*moduleThunkList).begin();
        while (iterator1 != (*moduleThunkList).end())
        {
            if (rva >= iterator1->second.firstThunk)
            {
                iterator1++;
                if (iterator1 == (*moduleThunkList).end())
                {
                    iterator1--;
                    module = &(iterator1->second);
                    break;
                }
                else if (rva < iterator1->second.firstThunk)
                {
                    iterator1--;
                    module = &(iterator1->second);
                    break;
                }
            }
            else
            {
#ifdef DEBUG_COMMENTS
                Scylla::debugLog.log(L"Error iterator1 != (*moduleThunkList).end()");
#endif
                break;
            }
        }
    }
    else
    {
        iterator1 = (*moduleThunkList).begin();
        module = &(iterator1->second);
    }

    if (!module)
    {
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL, rva);
#endif
        return false;
    }


    import.suspect = suspect;
    import.valid = valid;
    import.va = va;
    import.rva = rva;
    import.apiAddressVA = apiFound->va;
    import.ordinal = ordinal;
    import.hint = (WORD)apiFound->hint;

    wcscpy_s(import.moduleName, apiFound->module->getFilename());
    strcpy_s(import.name, apiFound->name);

    module->thunkList.insert(std::pair<DWORD_PTR,ImportThunk>(import.rva, import));

    return true;
}

void ApiReader::clearAll()
{
    minApiAddress = -1;
    maxApiAddress = 0;

    for ( stdext::hash_map<DWORD_PTR, ApiInfo *>::iterator it = apiList.begin(); it != apiList.end(); ++it )
    {
        delete it->second;
    }
    apiList.clear();

    if (moduleThunkList != 0)
    {
        (*moduleThunkList).clear();
    }
}

bool ApiReader::addNotFoundApiToModuleList(DWORD_PTR iatAddressVA, DWORD_PTR apiAddress)
{
    ImportThunk import;
    ImportModuleThunk  * module = 0;
    std::map<DWORD_PTR, ImportModuleThunk>::iterator iterator1;
    DWORD_PTR rva = iatAddressVA - targetImageBase;

    if ((*moduleThunkList).size() > 0)
    {
        iterator1 = (*moduleThunkList).begin();
        while (iterator1 != (*moduleThunkList).end())
        {
            if (rva >= iterator1->second.firstThunk)
            {
                iterator1++;
                if (iterator1 == (*moduleThunkList).end())
                {
                    iterator1--;
                    //new unknown module
                    if (iterator1->second.moduleName[0] == L'?')
                    {
                        module = &(iterator1->second);
                    }
                    else
                    {
                        addUnknownModuleToModuleList(rva);
                        module = &((*moduleThunkList).find(rva)->second);
                    }

                    break;
                }
                else if (rva < iterator1->second.firstThunk)
                {
                    iterator1--;
                    module = &(iterator1->second);
                    break;
                }
            }
            else
            {
#ifdef DEBUG_COMMENTS
                Scylla::debugLog.log(L"Error iterator1 != (*moduleThunkList).end()\r\n");
#endif
                break;
            }
        }
    }
    else
    {
        //new unknown module
        addUnknownModuleToModuleList(rva);
        module = &((*moduleThunkList).find(rva)->second);
    }

    if (!module)
    {
#ifdef DEBUG_COMMENTS
        Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL,rva);
#endif
        return false;
    }


    import.suspect = true;
    import.valid = false;
    import.va = iatAddressVA;
    import.rva = rva;
    import.apiAddressVA = apiAddress;
    import.ordinal = 0;

    wcscpy_s(import.moduleName, L"?");
    strcpy_s(import.name, "?");

    module->thunkList.insert(std::pair<DWORD_PTR,ImportThunk>(import.rva, import));

    return true;
}

bool ApiReader::isApiBlacklisted( const char * functionName )
{
    if (SystemInformation::currenOS < WIN_VISTA_32)
    {
        if (!strcmp(functionName, "RestoreLastError"))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }


    /*#ifdef _WIN64
    else if (SystemInformation::currenOS == WIN_XP_64 && !strcmp(functionName, "DecodePointer"))
    {
    	return true;
    }
    #endif*/
}

bool ApiReader::isWinSxSModule( ModuleInfo * module )
{
    if (wcsstr(module->fullPath, L"\\WinSxS\\"))
    {
        return true;
    }
    else if (wcsstr(module->fullPath, L"\\winsxs\\"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool ApiReader::isInvalidMemoryForIat( DWORD_PTR address )
{
    if (address == 0)
        return true;

    if (address == -1)
        return true;

    MEMORY_BASIC_INFORMATION memBasic = {0};

    if (VirtualQueryEx(ProcessAccessHelp::hProcess, (LPCVOID)address, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)))
    {
        if((memBasic.State == MEM_COMMIT) && ProcessAccessHelp::isPageAccessable(memBasic.Protect))
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    else
    {
        return true;
    }
}
